﻿namespace KeRui.Json.Converters
{
    using KeRui.Json;
    using KeRui.Json.Utilities;
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Xml;

    public class XmlNodeConverter : JsonConverter
    {
        private const string CDataName = "#cdata-section";
        private const string CommentName = "#comment";
        private const string DeclarationName = "?xml";
        private const string SignificantWhitespaceName = "#significant-whitespace";
        private const string TextName = "#text";
        private const string WhitespaceName = "#whitespace";

        public override bool CanConvert(Type valueType)
        {
            return typeof(XmlNode).IsAssignableFrom(valueType);
        }

        private void DeserializeNode(JsonReader reader, XmlDocument document, XmlNamespaceManager manager, XmlNode currentNode)
        {
            do
            {
                switch (reader.TokenType)
                {
                    case JsonToken.EndObject:
                    case JsonToken.EndArray:
                        return;

                    case JsonToken.PropertyName:
                    {
                        string propertyName = reader.Value.ToString();
                        reader.Read();
                        if (reader.TokenType != JsonToken.StartArray)
                        {
                            this.DeserializeValue(reader, document, manager, propertyName, currentNode);
                            break;
                        }
                        while (reader.Read() && (reader.TokenType != JsonToken.EndArray))
                        {
                            this.DeserializeValue(reader, document, manager, propertyName, currentNode);
                        }
                        break;
                    }
                    default:
                        throw new JsonSerializationException("Unexpected JsonToken when deserializing node: " + reader.TokenType);
                }
            }
            while ((reader.TokenType == JsonToken.PropertyName) || reader.Read());
        }

        private void DeserializeValue(JsonReader reader, XmlDocument document, XmlNamespaceManager manager, string propertyName, XmlNode currentNode)
        {
            string str9 = propertyName;
            if (str9 != null)
            {
                if (str9 == "#text")
                {
                    currentNode.AppendChild(document.CreateTextNode(reader.Value.ToString()));
                    return;
                }
                if (str9 == "#cdata-section")
                {
                    currentNode.AppendChild(document.CreateCDataSection(reader.Value.ToString()));
                    return;
                }
                if (str9 == "#whitespace")
                {
                    currentNode.AppendChild(document.CreateWhitespace(reader.Value.ToString()));
                    return;
                }
                if (str9 == "#significant-whitespace")
                {
                    currentNode.AppendChild(document.CreateSignificantWhitespace(reader.Value.ToString()));
                    return;
                }
            }
            if (propertyName[0] == '?')
            {
                if (propertyName == "?xml")
                {
                    string version = null;
                    string encoding = null;
                    string standalone = null;
                    while (reader.Read() && (reader.TokenType != JsonToken.EndObject))
                    {
                        str9 = reader.Value.ToString();
                        if (str9 == null)
                        {
                            goto Label_0178;
                        }
                        if (!(str9 == "@version"))
                        {
                            if (str9 == "@encoding")
                            {
                                goto Label_014E;
                            }
                            if (str9 == "@standalone")
                            {
                                goto Label_0163;
                            }
                            goto Label_0178;
                        }
                        reader.Read();
                        version = reader.Value.ToString();
                        goto Label_018E;
                    Label_014E:
                        reader.Read();
                        encoding = reader.Value.ToString();
                        goto Label_018E;
                    Label_0163:
                        reader.Read();
                        standalone = reader.Value.ToString();
                        goto Label_018E;
                    Label_0178:
                        throw new JsonSerializationException("Unexpected property name encountered while deserializing XmlDeclaration: " + reader.Value);
                    Label_018E:;
                    }
                    XmlDeclaration newChild = document.CreateXmlDeclaration(version, encoding, standalone);
                    currentNode.AppendChild(newChild);
                }
                else
                {
                    XmlProcessingInstruction instruction = document.CreateProcessingInstruction(propertyName.Substring(1), reader.Value.ToString());
                    currentNode.AppendChild(instruction);
                }
            }
            else
            {
                bool flag = false;
                bool flag2 = false;
                string prefix = this.GetPrefix(propertyName);
                Dictionary<string, string> dictionary = new Dictionary<string, string>();
                if (((((reader.TokenType != JsonToken.String) && (reader.TokenType != JsonToken.Null)) && ((reader.TokenType != JsonToken.Boolean) && (reader.TokenType != JsonToken.Integer))) && (reader.TokenType != JsonToken.Float)) && (reader.TokenType != JsonToken.Date))
                {
                    while ((!flag && !flag2) && reader.Read())
                    {
                        JsonToken tokenType = reader.TokenType;
                        if (tokenType != JsonToken.PropertyName)
                        {
                            if (tokenType != JsonToken.EndObject)
                            {
                                throw new JsonSerializationException("Unexpected JsonToken: " + reader.TokenType);
                            }
                        }
                        else
                        {
                            string key = reader.Value.ToString();
                            if (key[0] == '@')
                            {
                                string str7;
                                key = key.Substring(1);
                                reader.Read();
                                string str6 = reader.Value.ToString();
                                dictionary.Add(key, str6);
                                if (this.IsNamespaceAttribute(key, out str7))
                                {
                                    manager.AddNamespace(str7, str6);
                                }
                            }
                            else
                            {
                                flag = true;
                            }
                            goto Label_0302;
                        }
                        flag2 = true;
                    Label_0302:;
                    }
                }
                XmlElement element = !string.IsNullOrEmpty(prefix) ? document.CreateElement(propertyName, manager.LookupNamespace(prefix)) : document.CreateElement(propertyName);
                currentNode.AppendChild(element);
                foreach (KeyValuePair<string, string> pair in dictionary)
                {
                    string str8 = this.GetPrefix(pair.Key);
                    XmlAttribute newAttr = !string.IsNullOrEmpty(str8) ? document.CreateAttribute(pair.Key, manager.LookupNamespace(str8)) : document.CreateAttribute(pair.Key);
                    newAttr.Value = pair.Value;
                    element.SetAttributeNode(newAttr);
                }
                if (reader.TokenType == JsonToken.String)
                {
                    element.AppendChild(document.CreateTextNode(reader.Value.ToString()));
                }
                else if (reader.TokenType == JsonToken.Integer)
                {
                    element.AppendChild(document.CreateTextNode(XmlConvert.ToString((long) reader.Value)));
                }
                else if (reader.TokenType == JsonToken.Float)
                {
                    element.AppendChild(document.CreateTextNode(XmlConvert.ToString((double) reader.Value)));
                }
                else if (reader.TokenType == JsonToken.Boolean)
                {
                    element.AppendChild(document.CreateTextNode(XmlConvert.ToString((bool) reader.Value)));
                }
                else if (reader.TokenType == JsonToken.Date)
                {
                    element.AppendChild(document.CreateTextNode(XmlConvert.ToString((DateTime) reader.Value)));
                }
                else if ((reader.TokenType != JsonToken.Null) && !flag2)
                {
                    manager.PushScope();
                    this.DeserializeNode(reader, document, manager, element);
                    manager.PopScope();
                }
            }
        }

        private string GetPrefix(string qualifiedName)
        {
            int index = qualifiedName.IndexOf(':');
            if (((index == -1) || (index == 0)) || ((qualifiedName.Length - 1) == index))
            {
                return string.Empty;
            }
            return qualifiedName.Substring(0, index);
        }

        private string GetPropertyName(XmlNode node)
        {
            switch (node.NodeType)
            {
                case XmlNodeType.Element:
                    return node.Name;

                case XmlNodeType.Attribute:
                    return ("@" + node.Name);

                case XmlNodeType.Text:
                    return "#text";

                case XmlNodeType.CDATA:
                    return "#cdata-section";

                case XmlNodeType.ProcessingInstruction:
                    return ("?" + node.Name);

                case XmlNodeType.Comment:
                    return "#comment";

                case XmlNodeType.Whitespace:
                    return "#whitespace";

                case XmlNodeType.SignificantWhitespace:
                    return "#significant-whitespace";

                case XmlNodeType.XmlDeclaration:
                    return "?xml";
            }
            throw new JsonSerializationException("Unexpected XmlNodeType when getting node name: " + node.NodeType);
        }

        private bool IsNamespaceAttribute(string attributeName, out string prefix)
        {
            if (attributeName.StartsWith("xmlns", StringComparison.Ordinal))
            {
                if (attributeName.Length == 5)
                {
                    prefix = string.Empty;
                    return true;
                }
                if (attributeName[5] == ':')
                {
                    prefix = attributeName.Substring(6, attributeName.Length - 6);
                    return true;
                }
            }
            prefix = null;
            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType)
        {
            if (objectType != typeof(XmlDocument))
            {
                throw new JsonSerializationException("XmlNodeConverter only supports deserializing XmlDocuments");
            }
            XmlDocument document = new XmlDocument();
            XmlNamespaceManager manager = new XmlNamespaceManager(document.NameTable);
            reader.Read();
            this.DeserializeNode(reader, document, manager, document);
            return document;
        }

        private void SerializeGroupedNodes(JsonWriter writer, XmlNode node)
        {
            Dictionary<string, List<XmlNode>> dictionary = new Dictionary<string, List<XmlNode>>();
            int num = 0;
            while (num < node.ChildNodes.Count)
            {
                List<XmlNode> list;
                XmlNode node2 = node.ChildNodes[num];
                string propertyName = this.GetPropertyName(node2);
                if (!dictionary.TryGetValue(propertyName, out list))
                {
                    list = new List<XmlNode>();
                    dictionary.Add(propertyName, list);
                }
                list.Add(node2);
                num++;
            }
            foreach (KeyValuePair<string, List<XmlNode>> pair in dictionary)
            {
                List<XmlNode> list2 = pair.Value;
                if (list2.Count == 1)
                {
                    this.SerializeNode(writer, list2[0], true);
                }
                else
                {
                    writer.WritePropertyName(pair.Key);
                    writer.WriteStartArray();
                    for (num = 0; num < list2.Count; num++)
                    {
                        this.SerializeNode(writer, list2[num], false);
                    }
                    writer.WriteEndArray();
                }
            }
        }

        private void SerializeNode(JsonWriter writer, XmlNode node, bool writePropertyName)
        {
            switch (node.NodeType)
            {
                case XmlNodeType.Element:
                    if (writePropertyName)
                    {
                        writer.WritePropertyName(node.Name);
                    }
                    if ((CollectionUtils.IsNullOrEmpty(node.Attributes) && (node.ChildNodes.Count == 1)) && (node.ChildNodes[0].NodeType == XmlNodeType.Text))
                    {
                        writer.WriteValue(node.ChildNodes[0].Value);
                    }
                    else if ((node.ChildNodes.Count == 0) && CollectionUtils.IsNullOrEmpty(node.Attributes))
                    {
                        writer.WriteNull();
                    }
                    else
                    {
                        writer.WriteStartObject();
                        for (int i = 0; i < node.Attributes.Count; i++)
                        {
                            this.SerializeNode(writer, node.Attributes[i], true);
                        }
                        this.SerializeGroupedNodes(writer, node);
                        writer.WriteEndObject();
                    }
                    return;

                case XmlNodeType.Attribute:
                case XmlNodeType.Text:
                case XmlNodeType.CDATA:
                case XmlNodeType.ProcessingInstruction:
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                    if (writePropertyName)
                    {
                        writer.WritePropertyName(this.GetPropertyName(node));
                    }
                    writer.WriteValue(node.Value);
                    return;

                case XmlNodeType.Comment:
                    if (writePropertyName)
                    {
                        writer.WriteComment(node.Value);
                    }
                    return;

                case XmlNodeType.Document:
                case XmlNodeType.DocumentFragment:
                    this.SerializeGroupedNodes(writer, node);
                    return;

                case XmlNodeType.XmlDeclaration:
                {
                    XmlDeclaration declaration = (XmlDeclaration) node;
                    writer.WritePropertyName(this.GetPropertyName(node));
                    writer.WriteStartObject();
                    if (!string.IsNullOrEmpty(declaration.Version))
                    {
                        writer.WritePropertyName("@version");
                        writer.WriteValue(declaration.Version);
                    }
                    if (!string.IsNullOrEmpty(declaration.Encoding))
                    {
                        writer.WritePropertyName("@encoding");
                        writer.WriteValue(declaration.Encoding);
                    }
                    if (!string.IsNullOrEmpty(declaration.Standalone))
                    {
                        writer.WritePropertyName("@standalone");
                        writer.WriteValue(declaration.Standalone);
                    }
                    writer.WriteEndObject();
                    return;
                }
            }
            throw new JsonSerializationException("Unexpected XmlNodeType when serializing nodes: " + node.NodeType);
        }

        public override void WriteJson(JsonWriter writer, object value)
        {
            XmlNode node = value as XmlNode;
            if (node == null)
            {
                throw new ArgumentException("Value must be an XmlNode", "value");
            }
            writer.WriteStartObject();
            this.SerializeNode(writer, node, true);
            writer.WriteEndObject();
        }
    }
}

